package com.gitee.wsl.android.ui.base

import android.annotation.SuppressLint
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.graphics.PointF
import android.graphics.RectF
import android.os.Handler
import android.os.Message
import android.view.MotionEvent
import com.gitee.wsl.common.ui.base.Point
import com.gitee.wsl.ext.list.setAll
import com.gitee.wsl.mathematics.geometry.shape.Line
import java.text.DecimalFormat
import kotlin.random.Random

fun PointF.toPoint()= Point(this.x,this.y)

fun Point.toPointF()=PointF(this.x,this.y)

open class BezierLine(startPoint: PointF, endPoint: PointF): Line(startPoint.toPoint(), endPoint.toPoint()){

    val bezierPath: Path = Path() // 贝塞尔曲线路径

    protected var bezierPointList = mutableListOf<PointF>() // 贝塞尔曲线点集

    protected var bezierPoint: PointF? = null // 贝塞尔曲线移动点

    protected val controlPointList = mutableListOf<PointF>() // 控制点集

    // 切线点集
    protected var tangentPointList: ArrayList<ArrayList<ArrayList<PointF>>> = ArrayList()

    protected var instantTangentPointList: ArrayList<ArrayList<PointF>> = ArrayList()

    var mR = 0 // 移动速率

    var mRate = RATE // 速率

    var mState:Int  = STATE_READY or STATE_TOUCH // 状态

    var mLoop = false // 设置是否循环

    var mTangent = true // 设置是否显示切线

    var mWidth = 0
    var mHeight:Int = 0 // 画布宽高

    var mCurPoint: PointF? = null // 当前移动的控制点

    fun addControlPoints(vararg points: PointF){
        controlPointList.addAll(points)
        buildBezierPoints()
    }

    fun setKnotsPoint(pointF: List<PointF>){
        controlPointList.setAll(calculateControlPoint(pointF))
        buildBezierPoints()
    }

    /**
     * 创建Bezier点集
     *
     * @return
     */
    fun buildBezierPoints(): ArrayList<PointF> {
        val points = ArrayList<PointF>()
        val order = controlPointList.size - 1
        val delta = 1.0f / FRAME
        var t = 0f
        while (t <= 1) {
            // Bezier点集
            points.add(PointF(deCasteljauX(order, 0, t), deCasteljauY(order, 0, t)))
            t += delta
        }
        return points
    }

    /**
     * 创建切线点集
     */
    fun buildTangentPoints(): ArrayList<ArrayList<ArrayList<PointF>>> {
        var points: ArrayList<PointF> // 1条线点集
        var morepoints: ArrayList<ArrayList<PointF>> // 多条线点集
        val allpoints = ArrayList<ArrayList<ArrayList<PointF>>>() // 所有点集
        var point: PointF
        val order = controlPointList.size - 1
        val delta = 1.0f / FRAME
        for (i in 0 until order - 1) {
            val size = allpoints.size
            morepoints = ArrayList()
            for (j in 0 until order - i) {
                points = ArrayList()
                var t = 0f
                while (t <= 1) {
                    var p0x: Float
                    var p1x: Float
                    var p0y: Float
                    var p1y: Float
                    val z = (t * FRAME).toInt()
                    if (size > 0) {
                        p0x = allpoints[i - 1][j][z].x
                        p1x = allpoints[i - 1][j + 1][z].x
                        p0y = allpoints[i - 1][j][z].y
                        p1y = allpoints[i - 1][j + 1][z].y
                    } else {
                        p0x = controlPointList[j].x
                        p1x = controlPointList[j + 1].x
                        p0y = controlPointList[j].y
                        p1y = controlPointList[j + 1].y
                    }
                    val x = (1 - t) * p0x + t * p1x
                    val y = (1 - t) * p0y + t * p1y
                    point = PointF(x, y)
                    points.add(point)
                    t += delta
                }
                morepoints.add(points)
            }
            allpoints.add(morepoints)
        }
        return allpoints
    }

    /**
     * deCasteljau算法
     *
     * @param i 阶数
     * @param j 点
     * @param t 时间
     * @return
     */
    private fun deCasteljauX(i: Int, j: Int, t: Float): Float {
        return if (i == 1) {
            (1 - t) * controlPointList[j].x + t * controlPointList[j + 1].x
        } else
            (1 - t) * deCasteljauX(i - 1, j, t) + t * deCasteljauX(i - 1, j + 1, t)
    }

    /**
     * deCasteljau算法
     *
     * @param i 阶数
     * @param j 点
     * @param t 时间
     * @return
     */
    private fun deCasteljauY(i: Int, j: Int, t: Float): Float {
        return if (i == 1) {
            (1 - t) * controlPointList[j].y + t * controlPointList[j + 1].y
        } else
            (1 - t) * deCasteljauY(i - 1, j, t) + t * deCasteljauY(i - 1, j + 1, t)
    }

    /**
     * 判断坐标是否在合法区域中
     *
     * @param x
     * @param y
     * @return
     */
    fun isLegalTouchRegion(x: Float, y: Float): Boolean {
        if ((x <= REGION_WIDTH) || (x >= mWidth - REGION_WIDTH) || y <= REGION_WIDTH || y >= mHeight - REGION_WIDTH) {
            return false
        }
        val rectF = RectF()
        for (point: PointF in controlPointList) {
            if (mCurPoint != null && mCurPoint == point) { // 判断是否是当前控制点
                continue
            }
            rectF[point.x - REGION_WIDTH, point.y - REGION_WIDTH, point.x + REGION_WIDTH] =
                point.y + REGION_WIDTH
            if (rectF.contains(x, y)) {
                return false
            }
        }
        return true
    }

    /**
     * 获取合法控制点
     *
     * @param x
     * @param y
     * @return
     */
    fun getLegalControlPoint(x: Float, y: Float): PointF? {
        val rectF = RectF()
        for (point: PointF in controlPointList) {
            rectF[point.x - REGION_WIDTH, point.y - REGION_WIDTH, point.x + REGION_WIDTH] =
                point.y + REGION_WIDTH
            if (rectF.contains(x, y)) {
                return point
            }
        }
        return null
    }


    /**
     * 判断手指坐标是否在合法区域中
     *
     * @param x
     * @param y
     * @return
     */
    fun isLegalFingerRegion(x: Float, y: Float): Boolean {
        if (mCurPoint != null) {
            val rectF = RectF(
                mCurPoint!!.x - (FINGER_RECT_SIZE shr 1),
                mCurPoint!!.y - (FINGER_RECT_SIZE shr 1), mCurPoint!!.x +
                        (FINGER_RECT_SIZE shr 1), mCurPoint!!.y +
                        (FINGER_RECT_SIZE shr 1)
            )
            return rectF.contains(x, y)
        }
        return false
    }



    fun isReady(): Boolean {
        return (mState and STATE_READY) == STATE_READY
    }

    fun isRunning(): Boolean {
        return (mState and STATE_RUNNING) == STATE_RUNNING
    }

    fun isTouchable(): Boolean {
        return (mState and STATE_TOUCH) == STATE_TOUCH
    }

    fun isStop(): Boolean {
        return (mState and STATE_STOP) == STATE_STOP
    }

    /**
     * 开始
     */
    open fun start() {
        if (isReady()) {
            bezierPoint = null
            instantTangentPointList.clear()
            bezierPointList = buildBezierPoints()
            if (mTangent) {
                tangentPointList = buildTangentPoints()
            }
            mState = mState and STATE_READY.inv()
            mState = mState and STATE_TOUCH.inv()
            mState = mState or STATE_RUNNING
            //invalidate()
        }
    }

    /**
     * 停止
     */
    open fun stop() {
        if (isRunning()) {
            //mHandler.removeMessages(HANDLER_WHAT)
            mR = 0
            mState = mState and STATE_RUNNING.inv()
            mState = mState and STATE_STOP.inv()
            mState = mState or (STATE_READY or STATE_TOUCH)
            //invalidate()
        }
    }

    /**
     * 添加控制点
     */
    open fun addPoint(): Boolean {
        if (isReady()) {
            val size = controlPointList.size
            if (size >= MAX_COUNT + 1) {
                return false
            }
            val x = controlPointList[size - 1].x
            val y = controlPointList[size - 1].y
            val r = mWidth / 5
            val region = arrayOf(
                floatArrayOf(
                    0f,
                    r.toFloat()
                ), floatArrayOf(
                    0f,
                    -r.toFloat()
                ), floatArrayOf(
                    r.toFloat(),
                    r.toFloat()
                ), floatArrayOf(
                    -r.toFloat(),
                    -r.toFloat()
                ), floatArrayOf(
                    r.toFloat(),
                    0f
                ), floatArrayOf(
                    -r.toFloat(),
                    0f
                ), floatArrayOf(
                    0f,
                    1.5f * r
                ), floatArrayOf(
                    0f,
                    -1.5f * r
                ), floatArrayOf(
                    (1.5f * r),
                    1.5f * r
                ), floatArrayOf(
                    -1.5f * r,
                    -1.5f * r
                ), floatArrayOf(
                    1.5f * r,
                    0f
                ), floatArrayOf(
                    -1.5f * r,
                    0f
                ), floatArrayOf(
                    0f,
                    (2 * r).toFloat()
                ), floatArrayOf(
                    0f,
                    (-2 * r).toFloat()
                ), floatArrayOf(
                    (2 * r).toFloat(),
                    (2 * r).toFloat()
                ), floatArrayOf(
                    (-2 * r).toFloat(),
                    (-2 * r).toFloat()
                ), floatArrayOf(
                    (2 * r).toFloat(),
                    0f
                ), floatArrayOf(
                    (-2 * r).toFloat(),
                    0f
                )
            )
            var t = 0
            val len = region.size
            while (true) {  // 随机赋值
                t++
                if (t > len) {  // 超出region长度，跳出随机赋值
                    t = 0
                    break
                }
                val rand: Int = Random.nextInt(len)
                val px = x + region[rand][0]
                val py = y + region[rand][1]
                if (isLegalTouchRegion(px, py)) {
                    controlPointList.add(PointF(px, py))
                    //invalidate()
                    break
                }
            }
            if (t == 0) {   // 超出region长度而未赋值时，循环赋值
                for (floats: FloatArray in region) {
                    val px = x + floats[0]
                    val py = y + floats[1]
                    if (isLegalTouchRegion(px, py)) {
                        controlPointList.add(PointF(px, py))
                        //invalidate()
                        break
                    }
                }
            }
            return true
        }
        return false
    }

    /**
     * 删除控制点
     */
    open fun delPoint(): Boolean {
        if (isReady()) {
            val size = controlPointList.size
            if (size <= 2) {
                return false
            }
            controlPointList.removeAt(size - 1)
            //invalidate()
            return true
        }
        return false
    }

    /**
     * 贝塞尔曲线阶数
     *
     * @return
     */
    open fun getOrder(): Int {
        return controlPointList.size - 1
    }

    /**
     * 设置贝塞尔曲线阶数
     *
     * @param order
     */
    open fun setOrder(order: Int) {
        if (getOrder() == order) {
            return
        }
        stop()
        val size = getOrder() - order
        if (size > 0) {
            for (i in 0 until size) {
                delPoint()
            }
        } else if (size < 0) {
            for (i in -size downTo 1) {
                addPoint()
            }
        }
    }





    companion object{
        const val MAX_COUNT = 7 // 贝塞尔曲线最大阶数

        const val REGION_WIDTH = 30// 合法区域宽度

        const val FINGER_RECT_SIZE = 60 // 矩形尺寸

        const val BEZIER_WIDTH = 10 // 贝塞尔曲线线宽

        const val TANGENT_WIDTH = 6 // 切线线宽

        const val CONTROL_WIDTH = 12 // 控制点连线线宽

        const val CONTROL_RADIUS = 12 // 控制点半径

        const val TEXT_SIZE = 40 // 文字画笔尺寸

        const val TEXT_HEIGHT = 60 // 文本高度

        const val RATE = 10 // 移动速率

        const val HANDLER_WHAT = 100
        const val FRAME = 1000 // 1000帧

        val TANGENT_COLORS = arrayOf(
            "#7fff00",
            "#7a67ee",
            "#ee82ee",
            "#ffd700",
            "#1c86ee",
            "#8b8b00"
        ) // 切线颜色

        const val STATE_READY = 0x0001
        const val STATE_RUNNING = 0x0002
        const val STATE_STOP = 0x0004
        const val STATE_TOUCH = 0x0010

        const val SMOOTHNESS = 0.5f

        fun calculateControlPoint(pointList: List<PointF>):List<PointF> {
            val controlPointList= mutableListOf<PointF>()
            if (pointList.size <= 1) {
                return emptyList()
            }
            for ((i, point) in pointList.withIndex()) {
                when (i) {
                    0 -> {//第一项
                        //添加后控制点
                        val nextPoint = pointList[i + 1]
                        val controlX = point.x + (nextPoint.x - point.x) * SMOOTHNESS
                        val controlY = point.y
                        controlPointList.add(PointF(controlX, controlY))
                    }
                    pointList.size - 1 -> {//最后一项
                        //添加前控制点
                        val lastPoint = pointList[i - 1]
                        val controlX = point.x - (point.x - lastPoint.x) * SMOOTHNESS
                        val controlY = point.y
                        controlPointList.add(PointF(controlX, controlY))
                    }
                    else -> {//中间项
                        val lastPoint = pointList[i - 1]
                        val nextPoint = pointList[i + 1]
                        val k = (nextPoint.y - lastPoint.y) / (nextPoint.x - lastPoint.x)
                        val b = point.y - k * point.x
                        //添加前控制点
                        val lastControlX = point.x - (point.x - lastPoint.x) * SMOOTHNESS
                        val lastControlY = k * lastControlX + b
                        controlPointList.add(PointF(lastControlX, lastControlY))
                        //添加后控制点
                        val nextControlX = point.x + (nextPoint.x - point.x) * SMOOTHNESS
                        val nextControlY = k * nextControlX + b
                        controlPointList.add(PointF(nextControlX, nextControlY))
                    }
                }
            }
            return controlPointList
        }

        fun buildBezierLine(startX: Float, startY: Float,
                            endX: Float, endY: Float,vararg points: PointF
        ): Path {
            val line = BezierLine(PointF(startX,startY), PointF(endX,endY)).apply {
                addControlPoints(startPoint.toPointF())
                addControlPoints(*points)
                addControlPoints(endPoint.toPointF())
            }
            return line.bezierPath
        }

        fun buildBezierLineUseKnots(mControlPointList:List<PointF>): Path {
            val mValuePointList = calculateControlPoint(mControlPointList)
            val mPath = Path()
            val firstPoint = mValuePointList.first()
            //mPath.moveTo(firstPoint.x, height.toFloat())
            mPath.moveTo(firstPoint.x, firstPoint.y)
            for (i in 0 until mValuePointList.size * 2 - 2 step 2) {
                val leftControlPoint = mControlPointList[i]
                val rightControlPoint = mControlPointList[i + 1]
                val rightPoint = mValuePointList[i / 2 + 1]
                mPath.cubicTo(
                    leftControlPoint.x,
                    leftControlPoint.y,
                    rightControlPoint.x,
                    rightControlPoint.y,
                    rightPoint.x,
                    rightPoint.y
                )
            }
            //val lastPoint = mValuePointList.last()
            //填充渐变色
            //mPath.lineTo(lastPoint.x, height.toFloat())
            //mPath.lineTo(firstPoint.x, height.toFloat())
            return mPath
        }

    }

}


class BezierLineRender(startPoint: PointF, endPoint: PointF): BezierLine(startPoint, endPoint){
    // 贝塞尔曲线画笔
    private var mBezierPaint: Paint =  Paint().apply {
        color = Color.RED
        strokeWidth = BEZIER_WIDTH.toFloat()
        style = Paint.Style.STROKE
        isAntiAlias = true
    }

    // 移动点画笔
    private var mMovingPaint: Paint = Paint().apply {
        color = Color.BLACK
        isAntiAlias = true
        style = Paint.Style.FILL
    }

    // 控制点画笔
    private var mControlPaint: Paint = Paint().apply {
        color = Color.BLACK
        isAntiAlias = true
        style = Paint.Style.STROKE
    }
    // 切线画笔
    private var mTangentPaint: Paint = Paint().apply {
        color = Color.parseColor(TANGENT_COLORS[0])
        isAntiAlias = true
        setStrokeWidth(TANGENT_WIDTH.toFloat())
        style = Paint.Style.FILL
    }
    // 固定线画笔
    private var mLinePaint: Paint = Paint().apply {
        color = Color.LTGRAY
        strokeWidth = CONTROL_WIDTH.toFloat()
        isAntiAlias = true
        style = Paint.Style.FILL
    }
    // 点画笔
    private var mTextPointPaint: Paint = Paint().apply {
        color = Color.BLACK
        isAntiAlias = true
        textSize = TEXT_SIZE.toFloat()
    }
    // 文字画笔
    private var mTextPaint: Paint = Paint().apply {
        color = Color.GRAY
        isAntiAlias = true
        textSize = TEXT_SIZE.toFloat()
    }

    @SuppressLint("HandlerLeak")
    private val mHandler: Handler = object : Handler() {
        override fun handleMessage(msg: Message) {
            if (msg.what == HANDLER_WHAT) {
                mR += mRate
                if (mR >= bezierPointList.size) {
                    removeMessages(HANDLER_WHAT)
                    mR = 0
                    mState = mState and STATE_RUNNING.inv()
                    mState = mState and STATE_STOP.inv()
                    mState = mState or (STATE_READY or STATE_TOUCH)
                    if (mLoop) {
                        start()
                    }
                    return
                }
                if (mR != bezierPointList.size - 1 && mR + mRate >= bezierPointList.size) {
                    mR = bezierPointList.size - 1
                }
                // Bezier点
                bezierPoint = PointF(bezierPointList[mR].x, bezierPointList[mR].y)
                // 切线点
                if (mTangent) {
                    val size: Int = tangentPointList.size
                    var instantpoints: ArrayList<PointF>
                    instantTangentPointList.clear()
                    for (i in 0 until size) {
                        val len: Int = tangentPointList[i].size
                        instantpoints = ArrayList()
                        for (j in 0 until len) {
                            val x: Float = tangentPointList[i][j][mR].x
                            val y: Float = tangentPointList[i][j][mR].y
                            instantpoints.add(PointF(x, y))
                        }
                        instantTangentPointList.add(instantpoints)
                    }
                }
                if (mR == bezierPointList.size - 1) {
                    mState = mState or STATE_STOP
                }
                //invalidate()
            }
        }
    }


    @SuppressLint("DrawAllocation")
    fun onDraw(canvas: Canvas) {
        if (isRunning() && !isTouchable()) {
            if (bezierPoint == null) {
                bezierPath.reset()
                bezierPoint = bezierPointList[0]
                bezierPath.moveTo(bezierPoint!!.x, bezierPoint!!.y)
            }
            // 控制点和控制点连线
            val size = controlPointList.size
            var point: PointF
            for (i in 0 until size) {
                point = controlPointList[i]
                if (i > 0) {
                    // 控制点连线
                    canvas.drawLine(
                        controlPointList[i - 1].x, controlPointList[i - 1].y,
                        point.x, point.y,
                        mLinePaint
                    )
                }
                // 控制点
                canvas.drawCircle(point.x, point.y, CONTROL_RADIUS.toFloat(), mControlPaint)
                // 控制点文本
                canvas.drawText(
                    "p$i", point.x + CONTROL_RADIUS * 2,
                    point.y + CONTROL_RADIUS * 2, mTextPointPaint
                )
                // 控制点文本展示
                canvas.drawText(
                    (("p" + i + " ( " + DecimalFormat("##0.0").format(point.x)).toString() + " " +
                            ", " + DecimalFormat("##0.0").format(point.y)).toString() + ") ",
                    REGION_WIDTH.toFloat(),
                    (mHeight - (size - i) * TEXT_HEIGHT).toFloat(),
                    mTextPaint
                )
            }

            // 切线
            if (mTangent && (instantTangentPointList.isNotEmpty()) && !isStop()) {
                val tsize = instantTangentPointList.size
                var tps: ArrayList<PointF>
                for (i in 0 until tsize) {
                    tps = instantTangentPointList[i]
                    val tlen = tps.size
                    for (j in 0 until (tlen - 1)) {
                        mTangentPaint.color = Color.parseColor(TANGENT_COLORS[i])
                        canvas.drawLine(
                            tps[j].x, tps[j].y, tps[j + 1].x,
                            tps[j + 1].y,
                            mTangentPaint
                        )
                        canvas.drawCircle(
                            tps[j].x, tps[j].y, CONTROL_RADIUS.toFloat(),
                            mTangentPaint
                        )
                        canvas.drawCircle(
                            tps[j + 1].x, tps[j + 1].y, CONTROL_RADIUS.toFloat(),
                            mTangentPaint
                        )
                    }
                }
            }

            // Bezier曲线
            bezierPath.lineTo(bezierPoint!!.x, bezierPoint!!.y)
            canvas.drawPath(bezierPath, mBezierPaint)
            // Bezier曲线起始移动点
            canvas.drawCircle(bezierPoint!!.x, bezierPoint!!.y, CONTROL_RADIUS.toFloat(), mMovingPaint)
            // 时间展示
            canvas.drawText(
                "t:" + (DecimalFormat("##0.000").format(mR.toFloat() / FRAME)),
                (mWidth - TEXT_HEIGHT * 3).toFloat(), (mHeight - TEXT_HEIGHT).toFloat(), mTextPaint
            )
            //mHandler.removeMessages(HANDLER_WHAT)
            //mHandler.sendEmptyMessage(HANDLER_WHAT)
        }
        if (isTouchable()) {
            // 控制点和控制点连线
            val size = controlPointList.size
            var point: PointF
            for (i in 0 until size) {
                point = controlPointList[i]
                if (i > 0) {
                    canvas.drawLine(
                        controlPointList[i - 1].x, controlPointList[i - 1].y,
                        point.x, point.y,
                        mLinePaint
                    )
                }
                canvas.drawCircle(point.x, point.y, CONTROL_RADIUS.toFloat(), mControlPaint)
                canvas.drawText(
                    "p$i", point.x + CONTROL_RADIUS * 2,
                    point.y + CONTROL_RADIUS * 2, mTextPointPaint
                )
                canvas.drawText(
                    ((("p" + i + " ( " + DecimalFormat("##0.0").format(point.x)).toString() + " " +
                            ", " + DecimalFormat("##0.0").format(point.y)).toString() + ") "),
                    REGION_WIDTH.toFloat(),
                    (mHeight - (size - i) * TEXT_HEIGHT).toFloat(),
                    mTextPaint
                )
            }
        }
    }

    fun onTouchEvent(event: MotionEvent): Boolean {
        if (!isTouchable()) {
            return true
        }
        when (event.action) {
            MotionEvent.ACTION_DOWN -> mState = mState and STATE_READY.inv()
            MotionEvent.ACTION_MOVE -> {
                val x = event.x
                val y = event.y
                if (mCurPoint == null) {
                    mCurPoint = getLegalControlPoint(x, y)
                }
                if (mCurPoint != null && isLegalTouchRegion(x, y)) {  // 判断手指移动区域是否合法
                    if (isLegalFingerRegion(x, y)) {    // 判断手指触摸区域是否合法
                        mCurPoint!!.x = x
                        mCurPoint!!.y = y
                        //invalidate()
                    }
                }
            }

            MotionEvent.ACTION_UP -> {
                mCurPoint = null
                mState = mState or STATE_READY
            }
        }
        return true
    }
}